#region license
# This file is part of Grawlix.
# 
# Grawlix is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
# 
# Grawlix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public
# License along with Grawlix.  If not, see
# <http://www.gnu.org/licenses/>.
#endregion

"""A pool of tasks that supports waiting for all to complete."""

namespace ConcurrentBoo
import ConcurrentBoo.Executors
import ConcurrentBoo.Primitive
import System
import System.Threading

transient class ActionPool:
"""A pool that supports waiting for a number of concurrent tasks."""
	
	public event Error as EventHandler[of ErrorEventArgs]
	"""
	Handle additional exceptions past the first.
	Remarks: Not thread-safe. Provided only for logging purposes, etc.
	"""
	
	def constructor():
	"""Default constructor, using the ThreadPool."""
		self(ThreadPoolExecutor())
	
	def constructor([required] executor as IExecutor):
	"""
	Create a new pool.
	Param executor: The task executor to use.
	"""
		_executor = executor
	
	def Add([required] action as Action):
	"""
	Add a task to the pool.
	Param task: The task to execute.
	"""
		Announce()
		_executor.Execute() do:
			Complete(Invoke(action))
	
	def Wait():
	"""
	Wait until the tasks complete.
	Returns: true, always waiting for tasks to complete (or fail)
	Raises ConcurrentFailureException: For the first exception propagated.
	"""
		Wait(Timeout.Infinite)
	
	def Wait(timeout as timespan):
	"""
	Wait until the tasks have completed or the timeout runs out.
	Param timeout: The amount of time to wait before giving up.
	Returns: true if tasks complete, false if timeout runs out
	Raises ConcurrentFailureException: For the first exception propagated.
	"""
		Wait(timeout.Milliseconds)
	
	[lock(_sync)]
	def Wait(millisecondTimeout as int):
	"""
	Wait until the tasks have completed or the timeout runs out.
	Param millisecondTimeout: The amount of milliseconds to wait.
	Returns: true if tasks complete, false if timeout runs out
	Raises ConcurrentFailureException: For the first exception propagated.
	"""
		if _failure is not null:
			raise _failure
		if _count == 0:
			return true
		if not Monitor.Wait(_sync, millisecondTimeout):
			return false
		if _failure is not null:
			raise _failure
		return true
	
	[lock(_sync)]
	private def Announce():
	"""Notify the pool that a task has been added."""
		++_count
	
	private def Complete(computation as Computation):
	"""Notify the pool that a task has completed."""
		exception = computation.Failure
		if exception is not null:
			Failure(exception)
			return
		lock _sync:
			--_count
			assert _count >= 0
			if _count == 0:
				Monitor.PulseAll(_sync)
	
	private def Failure([required] exception as Exception):
	"""
	Notify the pool that a task has ended in exception.
	Any further attempt to wait will result in the exception being thrown.
	Param exception: The exception thrown.
	"""
		if _failure is not null:
			Error(self, ErrorEventArgs(exception))
			return
		lock _sync:
			_failure = exception
			Monitor.PulseAll(_sync)
	
	private _executor as IExecutor
	"""The task executor to use."""
	
	private _count as int
	"""A count of active tasks."""
	
	private _failure as Exception
	"""The exception thrown."""
	
	private _sync = object()
	"""The object to use to synchronize the pool."""
